package com.wlyuan.open.assembler;


import cn.hutool.crypto.SecureUtil;
import com.wlyuan.basic.dto.goods.GoodsBatchSpecDTO;
import com.wlyuan.core.named.TrackType;
import com.wlyuan.core.named.TransportMode;
import com.wlyuan.open.dto.order.OrderReceiptDelayReasonDTO;
import com.wlyuan.open.dto.order.OrderSignDelayReasonDTO;
import com.wlyuan.open.order.dto.OpenOrderCloseDTO;
import com.wlyuan.open.order.dto.OpenOrderCreationDTO;
import com.wlyuan.order.dto.OrderCloseDTO;
import com.wlyuan.order.dto.OrderCreationShouldDTO;
import com.wlyuan.order.dto.OrderGoodsDTO;
import com.wlyuan.order.dto.draft.OrderDraftCreationDTO;
import com.wlyuan.order.dto.draft.OrderDraftGoodsDTO;
import com.wlyuan.order.dto.receipt.OrderReceiptDelayMarkDTO;
import com.wlyuan.order.dto.rewrite.OrderRewriteDTO;
import com.wlyuan.order.dto.sign.OrderSignDelayMarkDTO;
import com.wlyuan.order.vo.OrderDetailVO;
import lombok.extern.slf4j.Slf4j;
import lombok.var;
import org.apache.commons.lang3.StringUtils;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.factory.Mappers;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 订单转化
 * OpenDTO -> 业务DTO
 */
@Slf4j
public class OrderAssembler {
    @Mapper(uses = {
            MappingOrder.class
    })
    public interface Assembler {

        OrderGoodsDTO assembleOrderGoods(OpenOrderCreationDTO.Spec spec);


        @MappingOrderCreation
        OrderCreationShouldDTO assemble(OpenOrderCreationDTO openOrder);

        @MappingOrderCreation
        @Mapping(target = "goodsList", source = "specs")
        OrderDraftCreationDTO assembleDraft(OpenOrderCreationDTO openOrder);

        @Mapping(target = "netWeight", source = "weight")
        List<OrderDraftGoodsDTO> assembleGoodsDraft(List<OpenOrderCreationDTO.Spec> spec);

        @Mapping(target = "netWeight", source = "weight")
        GoodsBatchSpecDTO assembleGoods(OpenOrderCreationDTO.Spec spec);

        List<GoodsBatchSpecDTO> assembleGoods(List<OpenOrderCreationDTO.Spec> specs);

        @Mapping(target = "sign", ignore = true)
        @Mapping(target = "receipt", ignore = true)
        @Mapping(target = "close", ignore = true)
        @Mapping(target = "device", ignore = true)
        @Mapping(target = "createAt", source = "createAt", dateFormat = "yyyy-MM-dd HH:mm:ss")
        @Mapping(target = "goodsList", ignore = true)
        void merge(@MappingTarget OrderRewriteDTO rewrite, OrderDetailVO orderDetail);

        @Mapping(target = "reason", source = "remarks")
        OrderCloseDTO assemble(OpenOrderCloseDTO openClose);

        OrderSignDelayMarkDTO assemble(OrderSignDelayReasonDTO source);

        OrderReceiptDelayMarkDTO assemble(OrderReceiptDelayReasonDTO source);
    }

    private static final Assembler ASSEMBLER = Mappers.getMapper(Assembler.class);

    public static OrderCreationShouldDTO assemble(OpenOrderCreationDTO openOrder, Map<String, Long> goodsMap) {
        OrderCreationShouldDTO creation = ASSEMBLER.assemble(openOrder);
        //经纬度
        if (creation != null && creation.getFromAddress() != null) {
            creation.getFromAddress().setLocation(openOrder.getFromAddress().getLon() + "," + openOrder.getFromAddress().getLat());
        }
        if (creation != null && creation.getToAddress() != null) {
            creation.getToAddress().setLocation(openOrder.getToAddress().getLon() + "," + openOrder.getToAddress().getLat());
        }
        //处理补差，补重的数据
        BigDecimal goodsTotalWeight = new BigDecimal("0");
        BigDecimal goodsTotalVolume = new BigDecimal("0");
        BigDecimal goodsTotalValue = new BigDecimal("0");
        List<OrderGoodsDTO> orderGoodsList = new ArrayList<>(openOrder.getSpecs().size());
        OrderGoodsDTO orderGoodsDTO = null;
        Map<Long, List<String>> goodsBatchNoMap = new HashMap<>();
        List<String> batchNoList = null;
        for (var goods : openOrder.getSpecs()) {
            var quantity = new BigDecimal(goods.getQuantity());
            var weight = goods.getWeight() == null ? new BigDecimal("0.0000") : goods.getWeight().setScale(4, RoundingMode.HALF_UP);
            var value = goods.getValue() == null ? new BigDecimal("0.0000") : goods.getValue().setScale(4, RoundingMode.HALF_UP);
            var volume = goods.getVolume() == null ? new BigDecimal("0.000000") : goods.getVolume().setScale(6, RoundingMode.HALF_UP);
            goodsTotalWeight = goodsTotalWeight.add(weight.multiply(quantity));
            goodsTotalVolume = goodsTotalVolume.add(volume.multiply(quantity));
            goodsTotalValue = goodsTotalValue.add(value.multiply(quantity));
            orderGoodsDTO = ASSEMBLER.assembleOrderGoods(goods);
            var goodsMapKey = OrderAssembler.assembleGoodsStamp(goods.getName(), goods.getType(), goods.getUnit(), goods.getSpec(), goods.getValue(), goods.getVolume(), goods.getWeight(), goods.getBarcode());
            logger.info("orderContractNo:{},goodsMapKey:{}", openOrder.getContractNo(), goodsMapKey);
            orderGoodsDTO.setGoodsId(goodsMap.get(goodsMapKey));
            //判断相同物品，批次号是否一致，如果一致，则新增#数字区分
            if (goodsBatchNoMap.get(orderGoodsDTO.getGoodsId()) != null) {
                String batchNo = orderGoodsDTO.getBatchNo() == null ? "" : orderGoodsDTO.getBatchNo();
                batchNoList = goodsBatchNoMap.get(orderGoodsDTO.getGoodsId());
                if (batchNoList.contains(batchNo)) {
                    long size = batchNoList.stream().filter(s -> s.equals(batchNo)).count();
                    orderGoodsDTO.setBatchNo(batchNo + "#" + size);
                    batchNoList.add(orderGoodsDTO.getBatchNo());
                }
                batchNoList.add(batchNo);
                goodsBatchNoMap.put(orderGoodsDTO.getGoodsId(), batchNoList);
            } else {
                List<String> goodsBatchNoList = new ArrayList<>();
                String batchNo = "";
                if (StringUtils.isNotBlank(orderGoodsDTO.getBatchNo())) {
                    batchNo = orderGoodsDTO.getBatchNo();
                }
                goodsBatchNoList.add(batchNo);
                goodsBatchNoMap.put(orderGoodsDTO.getGoodsId(), goodsBatchNoList);
            }
            orderGoodsList.add(orderGoodsDTO);
        }
        creation.setGoodsList(orderGoodsList);
        if (openOrder.getTotalWeight() != null) {
            creation.setDifferenceWeight(new BigDecimal(Double.toString(openOrder.getTotalWeight())).subtract(goodsTotalWeight));
        }
        if (openOrder.getTotalValue() != null) {
            creation.setDifferenceValue(new BigDecimal(Double.toString(openOrder.getTotalValue())).subtract(goodsTotalValue));
        }
        if (openOrder.getTotalVolume() != null) {
            creation.setDifferenceVolume(new BigDecimal(Double.toString(openOrder.getTotalVolume())).subtract(goodsTotalVolume));
        }
        if (StringUtils.isBlank(openOrder.getTrackingNo()) && creation.getTrackType() != null) {
            // 由于新增trackType =3的类型，允许trackingNo为空，所以在此处判断，如果trackType为，1，2的时候，设置trackType为null
            if (TrackType.isDevice(creation.getTrackType()) || TrackType.isExpress(creation.getTrackType())) {
                creation.setTrackType(null);
            }
        }
        return creation;
    }

    public static OrderDraftCreationDTO assembleDraft(OpenOrderCreationDTO openOrder) {
        OrderDraftCreationDTO creation = ASSEMBLER.assembleDraft(openOrder);
        //经纬度
        if (creation != null && creation.getFromAddress() != null) {
            creation.getFromAddress().setLocation(openOrder.getFromAddress().getLon() + "," + openOrder.getFromAddress().getLat());
        }
        if (creation != null && creation.getToAddress() != null) {
            creation.getToAddress().setLocation(openOrder.getToAddress().getLon() + "," + openOrder.getToAddress().getLat());
        }
        //处理补差，补重的数据
        BigDecimal goodsTotalWeight = new BigDecimal("0");
        BigDecimal goodsTotalVolume = new BigDecimal("0");
        BigDecimal goodsTotalValue = new BigDecimal("0");

        for (var goods : openOrder.getSpecs()) {
            var quantity = new BigDecimal(goods.getQuantity());
            var weight = goods.getWeight() == null ? new BigDecimal("0") : goods.getWeight().setScale(4, RoundingMode.HALF_UP);
            var value = goods.getValue() == null ? new BigDecimal("0") : goods.getValue().setScale(4, RoundingMode.HALF_UP);
            var volume = goods.getVolume() == null ? new BigDecimal("0") : goods.getVolume().setScale(6, RoundingMode.HALF_UP);
            goodsTotalWeight = goodsTotalWeight.add(weight.multiply(quantity));
            goodsTotalVolume = goodsTotalVolume.add(volume.multiply(quantity));
            goodsTotalValue = goodsTotalValue.add(value.multiply(quantity));
        }

        if (openOrder.getTotalWeight() != null) {
            creation.setDifferenceWeight(new BigDecimal(Double.toString(openOrder.getTotalWeight())).subtract(goodsTotalWeight));
        }
        if (openOrder.getTotalValue() != null) {
            creation.setDifferenceValue(new BigDecimal(Double.toString(openOrder.getTotalValue())).subtract(goodsTotalValue));
        }
        if (openOrder.getTotalVolume() != null) {
            creation.setDifferenceVolume(new BigDecimal(Double.toString(openOrder.getTotalVolume())).subtract(goodsTotalVolume));
        }
        if (openOrder.getTransport() != null && openOrder.getTransport().getMode() != null &&
                openOrder.getTransport().getMode().equals(TransportMode.DIVIDE.getValue())) {
            creation.setVehicle(null);
        }
        return creation;
    }

    public static List<GoodsBatchSpecDTO> assemble(Long customerId, List<OpenOrderCreationDTO.Spec> openOrderGoods) {
        var goods = ASSEMBLER.assembleGoods(openOrderGoods);
        BigDecimal zero = BigDecimal.ZERO;
        goods.stream().forEach(goodsBatchSpecDTO -> {
            goodsBatchSpecDTO.setCustomerId(customerId);
            if (goodsBatchSpecDTO.getVolume() != null) {
                goodsBatchSpecDTO.setVolume(goodsBatchSpecDTO.getVolume().setScale(6, RoundingMode.HALF_UP));
            } else {
                goodsBatchSpecDTO.setVolume(zero);
            }
            if (goodsBatchSpecDTO.getNetWeight() != null) {
                goodsBatchSpecDTO.setNetWeight(goodsBatchSpecDTO.getNetWeight().setScale(4, RoundingMode.HALF_UP));
            } else {
                goodsBatchSpecDTO.setNetWeight(zero);
            }
            if (goodsBatchSpecDTO.getValue() != null) {
                goodsBatchSpecDTO.setValue(goodsBatchSpecDTO.getValue().setScale(4, RoundingMode.HALF_UP));
            } else {
                goodsBatchSpecDTO.setValue(zero);
            }
        });
        return goods;
    }

    public static void merge(OrderRewriteDTO rewriteDTO, OrderDetailVO orderDetail) {
        ASSEMBLER.merge(rewriteDTO, orderDetail);
    }

    public static OrderCloseDTO assemble(OpenOrderCloseDTO openClose) {
        return ASSEMBLER.assemble(openClose);
    }

    public static String assembleGoodsStamp(String name, String type, String unit, String spec, BigDecimal value, BigDecimal volume, BigDecimal netWeight, String barcode) {
        DecimalFormat valueFormat = new DecimalFormat(".####");
        DecimalFormat volumeFormat = new DecimalFormat(".######");
        DecimalFormat weightFormat = new DecimalFormat(".####");
        var tempBarcode = null == barcode ? "" : barcode;
        var tempValue = null == value ? BigDecimal.ZERO : value.setScale(4, RoundingMode.HALF_UP);
        var tempVolume = null == volume ? BigDecimal.ZERO : volume.setScale(6, RoundingMode.HALF_UP);
        var tempWeight = null == netWeight ? BigDecimal.ZERO : netWeight.setScale(4, RoundingMode.HALF_UP);
        var tempUnit = null == unit ?"" :unit;
        String stamp = String.format("name=%s|type=%s|spec=%s|value=%s|volume=%s|netWeight=%s|unit=%s|barcode=%s",
                name, type, spec, valueFormat.format(tempValue),
                volumeFormat.format(tempVolume), weightFormat.format(tempWeight), tempUnit, tempBarcode);
        return SecureUtil.md5(stamp);
    }


    public static OrderSignDelayMarkDTO assemble(OrderSignDelayReasonDTO source) {
        return ASSEMBLER.assemble(source);
    }

    public static OrderReceiptDelayMarkDTO assemble(OrderReceiptDelayReasonDTO source) {
        return ASSEMBLER.assemble(source);
    }

}
